﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;

namespace SerialPortTool.Unity
{
    /// <summary>
    /// 串口处理类--->默认波特率是9600 ，数据位是8 ，校验位是 Parity.None  停止位是:StopBits.One 超时读取时间和超时写入时间分别是500
    /// </summary>
    public class SerialPortHand : SerialPortBase
    {
        SerialPort serial;

        public delegate void CallDataBack(TEventArgs<DataModel> args);

        /// <summary>
        /// 返回数据
        /// </summary>
        public event CallDataBack CallDataBackEvent;

        /// <summary>
        /// 串口正在关闭
        /// </summary>
        public bool SerialPortClosing { get; set; }

        /// <summary>
        /// 接收数据大小
        /// </summary>
        public int ReceiveByteCnt { get; set; }

        /// <summary>
        /// 发送数据大小
        /// </summary>
        public int SendByteCnt { get; set; }

        /// <summary>
        /// 显示方式 
        /// </summary>
        public bool IsUse16HexSHow { get; set; }

        /// <summary>
        /// 发送方式
        /// </summary>
        public bool IsUse16HexSend { get; set; }

        public bool IsOpen { get; set; }

        public SerialPortHand()
        {
            serial = new SerialPort();
            // 波特率
            serial.BaudRate = 9600;
            // 数据位
            serial.DataBits = 8;
            // 校验位
            serial.Parity = Parity.None;
            // 停止位
            serial.StopBits = StopBits.One;
            // 设置超时读取时间
            serial.ReadTimeout = 500;
            // 设置超时写入时间
            serial.WriteTimeout = 500;
            serial.DataReceived += Serial_DataReceived;
            serial.ErrorReceived += Serial_ErrorReceived;
        }

        private void Serial_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            string str = string.Empty;
            if (e.EventType == SerialError.Frame)
            {
                str = "成帧差错-->" + e.ToString();
            }
            else if (e.EventType == SerialError.Overrun)
            {
                str = "字符缓冲区溢出-->" + e.ToString();
            }
            else if (e.EventType == SerialError.RXOver)
            {
                str = "输入缓冲区溢出-->" + e.ToString();
            }
            else if (e.EventType == SerialError.RXParity)
            {
                str = "pada硬件校验错误-->" + e.ToString();
            }
            else if (e.EventType == SerialError.TXFull)
            {
                str = "传输数据，namun输出缓冲sedang penuh" + e.ToString();
            }
            CallDataBackEvent?.Invoke(new TEventArgs<DataModel>(new DataModel() { OperType = 1, Data1=str}));
        }

        private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (SerialPortClosing == true)  // 如果串口正在关闭，忽略操作，直接返回，尽快完成串口监听线程的一次循环
            {
                return;
            }
            try
            {
                Thread.Sleep(200);
                ReceiveByteCnt = serial.BytesToRead;  // 收计数
                if (IsUse16HexSHow) // 判定数据显示格式为十六进制
                {
                    byte[] buf = new byte[ReceiveByteCnt];   // 声明一个临时数组存储当前串口数据
                    serial.Read(buf, 0, ReceiveByteCnt);     // 读取缓冲数据
                    string tempstr = string.Empty;           // 声明一个没有分配空间的变量
                    for (int i = 0; i < ReceiveByteCnt; i++) // 遍历缓存数据
                    {
                        tempstr += buf[i].ToString("X2") + " ";  // 拼接十六进制字符串
                    }
                    CallDataBackEvent?.Invoke(new TEventArgs<DataModel>(new DataModel() { Data0 = buf, Data1 = tempstr, OperType = 0 }));
                }
                else
                {
                    serial.Encoding = System.Text.Encoding.GetEncoding("GB2312");  // 国标2312格式，防止出现乱码
                    CallDataBackEvent?.Invoke(new TEventArgs<DataModel>(new DataModel() { Data1 = serial.ReadExisting(), OperType = 0 }));
                }
                serial.DiscardInBuffer();  //清空接收缓冲区 --如果出现问题，可以考虑注释这个，再进行测试 
            }
            catch (Exception ex)
            {
                Logger.Error(string.Format("【SerialPortHand】-[Serial_DataReceived]-->{0}\r\n{1}",ex.Message,ex.StackTrace));
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="inputString"></param>
        public void SendData(string inputString) 
        {
            if (IsUse16HexSend)  //判定数据显示格式为十六进制显示
            {
                try
                {
                    inputString = inputString.Replace("0x", "").Replace(" ", ""); // 将十六进制字符串前的“0x”去掉
                    byte[] sendData = ToDoDataConvert.HexStringToByte(inputString);  // 将字符串转换成byte字符串放入数组
                    SendByteCnt = sendData.Length;     // 修改发计数，发送数据的长度
                    if (!serial.IsOpen)  // 判定串口为关闭状态
                    {
                        Logger.Error(string.Format("【SerialPortHand】-[SendData]-->串口未打开"));
                    }
                    else  // 串口为打开状态
                    {
                        serial.Write(sendData, 0, sendData.Length);  // 要发送的数据写入缓冲区
                        string _log = string.Format("【发送数据】[16进制]: {0}", inputString);
                        Logger.Info(_log);
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error(string.Format("【SerialPortHand】-[SendData]-->{0}\r\n{1}", ex.Message, ex.StackTrace));
                }
            }
            else  // 直接以字符形式发送 
            {
                try
                {
                    char[] SendChars = inputString.ToCharArray();  // 将字符串转换成字符存入字符数组
                    int CharsLength = SendChars.Length;      // 声明字符数组的长度
                    SendByteCnt = CharsLength;       // 修改发计数
                    for (int i = 0; i < CharsLength; i++)  // 遍历字符数组，
                    {
                        if (SendChars[i] >= 0x4e00 && SendChars[i] <= 0x9fa5)  // 判定字符不是汉字（汉字编码区间为0x4e00--0x9fa5）
                        {
                            SendByteCnt++;   // 发计数自增
                        }
                    }
                    serial.Encoding = System.Text.Encoding.GetEncoding("GB2312");  // 国标2312格式，防止出现乱码
                    serial.Write(SendChars, 0, CharsLength);  // 字符数组写入缓冲区
                    string _log = string.Format("【发送数据】[直接字符串发送]: {0}", inputString);
                    Logger.Info(_log);
                }
                catch (Exception ex)
                {
                    Logger.Error(string.Format("【SerialPortHand】-[SendData]-->{0}\r\n{1}", ex.Message, ex.StackTrace));
                }
            }
        }

        /// <summary>
        /// 设置串口号
        /// </summary>
        /// <param name="portName"></param>
        public void SetPortName(string portName)
        {
            serial.PortName = portName;
        }

        /// <summary>
        /// 设置波特率
        /// </summary>
        public void SetBaudRate(SerialPortBaudRates baudRates)
        {
            serial.BaudRate = (int)baudRates;
        }

        /// <summary>
        /// 设置数据位
        /// </summary>
        public void SetDataBits(SerialPortDatabits databits)
        {
            serial.DataBits = (int)databits;
        }

        /// <summary>
        /// 设置校验位
        /// </summary>
        public void SetParity(Parity parity)
        {
            serial.Parity = parity;
        }

        /// <summary>
        /// 设置停止位
        /// </summary>
        public void SetStopBits(StopBits stopBits)
        {
            serial.StopBits = stopBits;
        }

        /// <summary>
        /// 设置超时读取时间
        /// </summary>
        public void SetReadTimeout(int timeOut)
        {
            serial.ReadTimeout = timeOut;
        }

        /// <summary>
        /// 设置超时写入时间
        /// </summary>
        public void SetWriteTimeout(int timeOut)
        {
            serial.WriteTimeout = timeOut;
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        public void OpenSerialPort()
        {
            if (!serial.IsOpen)
            {
                serial.Open();
                IsOpen = true;
                SerialPortClosing = false;
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public void CloseSerialPort()
        {
            if (serial.IsOpen)
            {
                serial.Close();
                IsOpen = false;
                SerialPortClosing = true;
            }
        }
    }
}
